package appx.craft.droid.services;

import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import appx.craft.droid.utils.Const;
import appx.craft.droid.utils.Pref;
import appx.craft.droid.utils.Utility;

public class LocationListeningServices extends Service implements LocationListener{
	
	private static final String TAG = LocationListeningServices.class.getCanonicalName();
	
	static final int TWO_MINUTES = 1000 * 60 * 2;
	
	 // The minimum distance to change Updates in meters
	static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
 
    // The minimum time between updates in milliseconds
    static final long MIN_TIME_BW_UPDATES = 1000 * 1 ; // 1 seconds 
 
	
	LocationManager mlocation$Manager = null;
	LocationProvider mlocation$Provider = null;
	
	public LocationListeningServices() {
		
	}
	
	/**
	 * Create IBinder for this service 
	 */
	private final IBinder mobject$Binder = new LocalBinder();
	/**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
    	
        public LocationListeningServices getService() {
            // Return this instance of LocalService so clients can call public methods which is show in this service getRandomNumber
            return LocationListeningServices.this;
        }
    }
	
	
	@Override
	public void onCreate() {
		super.onCreate();
		Log.d(TAG,"[ onCreate ]");
		
		/**
		 * Acquire a reference to the system Location Manager
		 */
		mlocation$Manager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
		
		Log.d(TAG,"[Create LocationManager ]");
		
		
		if(Utility.isOnline()){
			
			
			/**
			 * Android's Network Location Provider 
			 */
			mlocation$Provider = mlocation$Manager.getProvider(LocationManager.NETWORK_PROVIDER);
			
			Log.d(TAG,"[ Get Service Provide : LocationManager.NETWORK_PROVIDER ]");
			
		}else {
			
			/**
			 * // Or use LocationManager.GPS_PROVIDER
			 * 
			 */
			mlocation$Provider  = mlocation$Manager.getProvider(LocationManager.GPS_PROVIDER);
			
			Log.d(TAG,"[ Get Service Provide : LocationManager.GPS_PROVIDER ]");
			
		}
		
		/**
		 * Register the listener with the Location Manager to receive location updates
		 */
		
		mlocation$Manager.requestLocationUpdates(mlocation$Provider.getName(),MIN_TIME_BW_UPDATES,MIN_DISTANCE_CHANGE_FOR_UPDATES,this);
		Log.d(TAG,"[ Register the listener with the Location Manager ] " + mlocation$Provider.getName());
		
		if (mlocation$Manager != null) {
			if(mlocation$Provider != null){
				
				Location mTestlocation = null;
				if(mlocation$Provider.getName().equalsIgnoreCase(LocationManager.NETWORK_PROVIDER)){
					mTestlocation = mlocation$Manager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            	}else if(mlocation$Provider.getName().equalsIgnoreCase(LocationManager.GPS_PROVIDER)){
            		mTestlocation = mlocation$Manager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
				}
				if (mTestlocation != null) {
	            	
            		Log.d(TAG,"[ Latitude  ]" + mTestlocation.getLatitude());
            		Log.d(TAG,"[ Longitude ]" + mTestlocation.getLongitude());
            		Pref.setValue(Const.CURRENT_LATITUDE,String.valueOf(mTestlocation.getLatitude()));
        			Pref.setValue(Const.CURRENT_LONGITUDE,String.valueOf(mTestlocation.getLongitude()));
            	}
	
			}
		}
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.d(TAG,"[ onDestroy ]");
	}
	
	public void startLocationUpdate(){
		
		/**
		 * Register the listener with the Location Manager to receive location updates
		 */
		
		mlocation$Manager.requestLocationUpdates(mlocation$Provider.getName(),0, 0, this);
		Log.d(TAG,"[ Register the listener with the Location Manager ]");
		
	}
	
	public void removeUpdatesService(){
		
		Log.d(TAG,"[ Remove the listener you previously added ]");
		mlocation$Manager.removeUpdates(this);
		
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		
		 // TODO: Return the communication channel to the service.
        // IF You define Binder Object then return Object of Binder
    	// else case uncommit following code
    		//throw new UnsupportedOperationException("Not yet implemented");
    	Log.d(TAG,"[ ON BIND CALLED ]");
    	
    	return mobject$Binder;
	}
	
	

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		Log.d(TAG," [ onStatusChanged ] ");
		Toast.makeText(getApplicationContext(),"onStatusChanged", 1000*1).show();
	}
	
	@Override
	public void onProviderEnabled(String provider) {
		Log.d(TAG," [ onProviderEnabled ] ");
		Toast.makeText(getApplicationContext(),"onProviderEnabled", 1000*1).show();
		
	}
	
	@Override
	public void onProviderDisabled(String provider) {
		Log.d(TAG," [ onProviderDisabled ] ");
		showSettingsAlert();
	}
	
	@Override
	public void onLocationChanged(Location location) {
		Log.d(TAG,"[ $$$$$   onLocationChanged  ]");
		Toast.makeText(getApplicationContext(),"GOT NEW LOCATION", 1000*1).show();
		/**
		 *  Called when a new location is found by the location provider.
		 */
		makeUseOfNewLocation(location);
	}
	
	/**
	 *  set accurate location in- side pref for later used it 
	 * @param currentBestLocation
	 */
	public void makeUseOfNewLocation(Location currentBestLocation){
		
		Location mLastKnowLocation = GetFastFixLastLocation();
		Location mCurrentBestLocation = currentBestLocation;
		
		/**
		 * This will help to Determines good quality of location from both  
		 */
		boolean isNewLocationIsBest = isBetterLocation(mLastKnowLocation, mCurrentBestLocation);
		
		if(isNewLocationIsBest){
			/**
			 * store location inside Preference 
			 */
			Log.d(TAG,"[mCurrentBestLocation  Longitude ] :" + mCurrentBestLocation.getLongitude());
			Log.d(TAG,"[mCurrentBestLocation  Latitude 	] :" + mCurrentBestLocation.getLatitude());
			
			Pref.setValue(Const.CURRENT_LATITUDE,String.valueOf(mCurrentBestLocation.getLatitude()));
			Pref.setValue(Const.CURRENT_LONGITUDE,String.valueOf(mCurrentBestLocation.getLongitude()));
			
		}else{
			
			Log.d(TAG,"[ mLastKnowLocation Longitude ] :" + mLastKnowLocation.getLongitude());
			Log.d(TAG,"[ mLastKnowLocation Latitude 	] :" + mLastKnowLocation.getLatitude());
			
			Pref.setValue(Const.CURRENT_LATITUDE,String.valueOf(mLastKnowLocation.getLatitude()));
			Pref.setValue(Const.CURRENT_LONGITUDE,String.valueOf(mLastKnowLocation.getLongitude()));
			
		}
		
	}
	
	
	/** Determines whether one Location reading is better than the current Location fix
	  * @param location  The new Location that you want to evaluate
	  * @param currentBestLocation  The current Location fix, to which you want to compare the new one
	  */
	protected boolean isBetterLocation(Location location, Location currentBestLocation) {
	    if (currentBestLocation == null) {
	        // A new location is always better than no location
	        return true;
	    }

	    // Check whether the new location fix is newer or older
	    long timeDelta = location.getTime() - currentBestLocation.getTime();
	    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
	    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
	    boolean isNewer = timeDelta > 0;

	    // If it's been more than two minutes since the current location, use the new location
	    // because the user has likely moved
	    if (isSignificantlyNewer) {
	        return true;
	    // If the new location is more than two minutes older, it must be worse
	    } else if (isSignificantlyOlder) {
	        return false;
	    }

	    // Check whether the new location fix is more or less accurate
	    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
	    boolean isLessAccurate = accuracyDelta > 0;
	    boolean isMoreAccurate = accuracyDelta < 0;
	    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

	    // Check if the old and new location are from the same provider
	    boolean isFromSameProvider = isSameProvider(location.getProvider(),
	            currentBestLocation.getProvider());

	    // Determine location quality using a combination of timeliness and accuracy
	    if (isMoreAccurate) {
	        return true;
	    } else if (isNewer && !isLessAccurate) {
	        return true;
	    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
	        return true;
	    }
	    return false;
	}

	/** Checks whether two providers are the same */
	private boolean isSameProvider(String provider1, String provider2) {
	    if (provider1 == null) {
	      return provider2 == null;
	    }
	    return provider1.equals(provider2);
	}
	
	
	private Location GetFastFixLastLocation(){
		
		if(mlocation$Manager != null && mlocation$Provider != null){
			return mlocation$Manager.getLastKnownLocation(mlocation$Provider.getName());	
		}
		
		return null;
	}
	
	/**
     * Function to show settings alert dialog
     * */
    public void showSettingsAlert(){
    	
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(Const.CONTEXT);
 
        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");
 
        // Setting Dialog Message
        alertDialog.setMessage("Please enable your GPS/network provider from setting menu of your phone !!!");
 
       
       /* // On pressing Settings button
        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog,int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(intent);
            }
        });*/
 
        // on pressing cancel button
        alertDialog.setNegativeButton("Dismiss", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
            }
        });
 
        // Showing Alert Message
        alertDialog.show();
    }
	
}
